package org.jflame.commons.crypto.digest;

import org.jflame.commons.codec.Transcoder;
import org.jflame.commons.crypto.BC;
import org.jflame.commons.crypto.EncryptException;
import org.jflame.commons.util.CharsetHelper;

import java.security.DigestException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/**
 * HMAC算法工具类
 * <p>
 * HMAC（Hash-based Message Authentication Code）是一种使用密码散列函数，同时结合一个加密密钥，<br>
 * 通过特别计算方式之后产生的消息认证码（MAC）。它可以用来保证数据的完整性，同时可以用来作某个消息的身份验证
 * 
 * @see HmacAlg
 * @author charles.zhang
 */
public final class Hmac {

    /**
     * 计算hmac算法消息摘要
     * 
     * @param algorithm
     * @param plainBytes
     * @param keyBytes
     * @return
     * @throws DigestException
     */
    public static byte[] hmac(HmacAlg algorithm, byte[] plainBytes, byte[] keyBytes) throws DigestException {
        try {
            // HmacSM3需要高版本BC包,低版本有sm3算法但没有注册HmacSM3名到Provider
            Mac mac = algorithm == HmacAlg.HmacSM3 ? Mac.getInstance(algorithm.name(), BC.addProvider())
                    : Mac.getInstance(algorithm.name());
            SecretKeySpec secretKey = new SecretKeySpec(keyBytes, mac.getAlgorithm());
            mac.init(secretKey);
            // 执行消息摘要 data是摘要后的结果
            byte[] data = mac.doFinal(plainBytes);
            return data;
        } catch (EncryptException e) {
            throw new DigestException(e.getMessage());
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new DigestException(e);
        }
    }

    /**
     * HmacMD5加密字节数组,指定密钥.
     * 
     * @param plainBytes 明文
     * @param keyBytes 密钥
     * @return 密文字节数组
     * @throws EncryptException 加密异常
     */
    public static byte[] hmacMD5(byte[] plainBytes, byte[] keyBytes) throws DigestException {
        return hmac(HmacAlg.HmacMD5, plainBytes, keyBytes);
    }

    /**
     * 计算HmacMD5消息摘要
     * 
     * @param plainText 明文
     * @param key 密钥
     * @return 十六进制编码的摘要
     * @throws EncryptException 加密异常
     */
    public static String hmacMD5Hex(String plainText, String key) throws DigestException {
        byte[] macData = hmacMD5(CharsetHelper.getUtf8Bytes(plainText), CharsetHelper.getUtf8Bytes(key));
        return Transcoder.encodeHexString(macData);
    }

    /**
     * 计算hmacSHA1消息摘要
     * 
     * @param plainText 明文
     * @param key 密钥
     * @return 十六进制编码的摘要
     * @throws EncryptException
     */
    public static String hmacSHAHex(String plainText, String key) throws DigestException {
        byte[] macData = hmac(HmacAlg.HmacSHA1, CharsetHelper.getUtf8Bytes(plainText), CharsetHelper.getUtf8Bytes(key));
        return Transcoder.encodeHexString(macData);
    }

    /**
     * 计算hmacSHA1消息摘要
     * 
     * @param plainText 明文
     * @param key 密钥
     * @return
     * @throws EncryptException
     */
    public static byte[] hmacSHA(String plainText, String key) throws DigestException {
        byte[] macData = hmac(HmacAlg.HmacSHA1, CharsetHelper.getUtf8Bytes(plainText), CharsetHelper.getUtf8Bytes(key));
        return macData;
    }

    /**
     * 计算hmacSHA256消息摘要
     * 
     * @param plainText 明文
     * @param key 密钥
     * @return 密文hex
     * @throws EncryptException
     */
    public static String hmacSHA256(String plainText, String key) throws DigestException {
        byte[] macData = hmac(HmacAlg.HmacSHA256, CharsetHelper.getUtf8Bytes(plainText),
                CharsetHelper.getUtf8Bytes(key));
        return Transcoder.encodeHexString(macData);
    }

    /**
     * 计算hmacSHA512消息摘要
     * 
     * @param plainText 明文
     * @param key 密钥
     * @return 密文hex
     * @throws EncryptException
     */
    public static String hmacSHA512(String plainText, String key) throws DigestException {
        byte[] macData = hmac(HmacAlg.HmacSHA512, CharsetHelper.getUtf8Bytes(plainText),
                CharsetHelper.getUtf8Bytes(key));
        return Transcoder.encodeHexString(macData);
    }

    /**
     * 计算hmacSM3消息摘要
     * 
     * @param plainText 明文
     * @param key 密钥
     * @return 密文hex
     * @throws EncryptException
     */
    public static String hmacSM3(String plainText, String key) throws DigestException {
        byte[] macData = hmac(HmacAlg.HmacSM3, CharsetHelper.getUtf8Bytes(plainText), CharsetHelper.getUtf8Bytes(key));
        return Transcoder.encodeHexString(macData);
    }
}
